<html>
<head>
<title>CpcPaChi - Version 0.11</title>
<style type="text/css">
span {
  font-family: 'Courier New';
  font-size: 10pt;
  color: #000000;
}
.sc0 {
  color: #808080;
}
.sc3 {
  color: #008080;
}
.sc4 {
  color: #FF8000;
}
.sc5 {
  font-weight: bold;
  color: #0000FF;
}
.sc7 {
  color: #808080;
}
.sc9 {
  color: #804000;
}
.sc10 {
  font-weight: bold;
  color: #000080;
}
.sc11 {
}
</style>
</head>
<body>
<h2>Un compilador sencillo paso a paso (11 - Saltos incondicionales)</h2>

<p align="right">(Volver al <a href="doc.html">&iacute;ndice</a>) </p>

<p>Las estructuras condicionales, como &quot;if&quot;, y las repetitivas como
&quot;while&quot; y &quot;for&quot;, cuando se conviertan a c&oacute;digo m&aacute;quina,
necesitar&aacute;n realizar saltos a partes anteriores o posteriores
del programa. Por eso, como forma de acercarnos a esa problem&aacute;tica, vamos
a crear saltos incondicionales: la peligrosa orden &quot;goto&quot;.</p>

<p>Eso supone que tambi&eacute;n podamos definir etiquetas, para indicar a
qu&eacute; lugar queremos saltar. En Pascal, quedar&iacute;a algo como:</p>

<pre><code>label destino;
begin
  ...
  destino:
  ...
  goto destino;
  ...
end.</code></pre>

<p>La forma m&aacute;s sencilla (aunque poco eficiente) de convertirlo a
c&oacute;digo m&aacute;quina es usar saltos a direcciones absolutas de memoria.
Por ejemplo, un salto atr&aacute;s en ensamblador quedar&iacute;a as&iacute;:</p>

<pre><code>
  ...
  .destino
  ...
  jp destino
  ...
</code></pre>

<p>La traducci&oacute;n a ensamblador es pr&aacute;cticamente inmediata.
Pero nosotros no queremos compilar a ensamblador, sino directamente a
c&oacute;digo m&aacute;quina, de modo que no podemos emplear etiquetas, sino que
debemos indicar a qu&eacute; posicion de memoria queremos saltar. Si se trata
de una posici&oacute;n de memoria anterior, no es d&iacute;ficil: como ya la
conocemos, podr&iacute;amos construir sin problemas la orden de salto:
</p>

<pre><code>
  30000: ...
  ...
  C3 30 75; jp 30000
  ...
</code></pre>

<p>Pero si se trata de un salto hacia un punto posterior, estamos atados
de manos: todav&iacute;a no sabemos de qu&eacute; direcci&oacute;n de memoria se trata, lo que
nos obliga a &quot;retroceder&quot; cuando ya la conozcamos: en ensamblador ser&iacute;a
</p>

<pre><code>
  ...
  jp destino
  ...
  .destino
  ...
</code></pre>

<p>Que en c&oacute;digo m&aacute;quina se convertir&iacute;a en:
</p>

<pre><code>
  ...
  C3 ?? ?? ; jp ????
  ...
</code></pre>

<p>Esas &quot;interrogaciones&quot; representan lo que todav&iacute;
a no sabemos, huecos que tendremos que rellenar m&aacute;s
adelante, y que nos obligar&aacute;n a dar una segunda pasada
(aunque sencilla). Como estamos volcando a fichero a medida que
compilamos, tenemos dos opciones</p>

<ul>
  <li>Conservar todo el programa en memoria, para poder dar la segunda
    pasada antes de volcar a disco.</li>
  <li>Tras terminar la primera pasada, volver a abrir el fichero generado
    para dar la segunda pasada, volcando el resultado a un nuevo fichero.</li>
</ul>

<p>La primera opci&oacute;n ser&iacute;a la m&aacute;s eficiente en
cuanto a velocidad, pero necesita m&aacute;s espacio en memoria 
y hacer m&aacute;s cambios al generador de c&oacute;digo, as&iacute; que
seguiremos volcando a disco, para dar una segunda pasada sobre ese
fichero resultante en disco.
</p>

<p>As&iacute;, el resultado de la primera salida para esa instrucci&oacute;n de
salto podr&iacute;a ser algo como:
</p>

<pre><code>
  ...
  DATA,C3,destinoLOW,destinoHIGH ; jp destino
  ...
</code></pre>

<p>(Lo de descomponer &quot;destino&quot; en &quot;destinoLOW&quot; y &quot;destinoHIGH&quot; es
porque en la m&aacute;quina que estamos tomando como destino se sigue
el criterio habitual de codificar los datos de m&aacute;s de un byte
como el byte menos significativo seguido por el m&aacute;s significativo
-&quot;little endian&quot;).
</p>

<p>La segunda pasada podr&iacute;a buscar cada uno de los identificadores
de la tabla de s&iacute;mbolos (como &quot;destino&quot;) para ver si aparecen
descompuestos en &quot;destinoLOW&quot; y &quot;destinoHIGH&quot;
y reemplazarlos por sus valores.
</p>

<p>Ya que vamos a dar dos pasadas, podemos optimizar algo que antes
hab&iacute;amos hecho de una forma sencilla pero no demasiado correcta:
podemos dejar las variables a continuaci&oacute;n del c&oacute;digo, en vez
de colocarlas al principio y separadas del c&oacute;digo por un espacio
arbitrario. Ahora otros fragmentos de nuestro c&oacute;digo ser&aacute;n cosas como:
</p>

<pre><code>
  ...
  DATA,3E,[xBYTE] ; LD A, variableX
  ...
</code></pre>

<p>Y el espacio para esa &quot;variableX&quot; se reservar&iacute;a al final, de la
primera pasada, tras el c&oacute;digo del programa, y el valor exacto
de la direcci&oacute;n se sustituir&iacute;a en la segunda pasada.</p>

<p>Aun as&iacute;, esa mejora de momento la aplazamos para algo m&aacute;s adelante...</p>

<p><br />Todo esto afecta en los siguientes cambios al c&oacute;digo fuente:</p>

<p>Por una parte, deberemos reconocer la orden LABEL, que declara una direcci&oacute;n de salto. Como esta direcci&oacute;n todav&iacute;a no es conocida, le damos 0 como valor inicial:</p>

<pre><code>
void analizarLABEL()
{
  string identificador = miLexer.ObtenerIdentificador();
  miTablaSimb.Insertar(identificador, tipoId.LABEL,
    tamDato.INTEGER, 0 );
  char terminador = miLexer.LeerCualquierSimbolo();
  if ( (terminador != ';') &amp;&amp; (terminador != ',') )
    miLexer.ErrorFatal(&quot;Terminador incorrecto: {0}&quot; + terminador);

  if (terminador == ',')
    analizarLABEL();
}
</code></pre>

<p>Adem&aacute;s, debemos ver cu&aacute;ndo aparece realmente esa variable en el c&oacute;digo. Cuando encontremos una palabra en nuestro c&oacute;digo fuente que no sea una palabra reservada, podr&aacute; tratarse de una asignaci&oacute;n o de una etiqueta:</p>

<pre><code>
void analizarLABELoASIGN( string identif )
{
  // Provisional: no deberia permitir espacio entre : y =
  miLexer.LeerSimbolo(':');
  char siguiente = miLexer.LeerCualquierSimbolo();
  if (siguiente == '=')
  {
    int valor = miLexer.ObtenerEntero();
    miLexer.LeerSimbolo(';');
    miGenerador.genASIGN(identif,valor);
  }
  else
  {
    miGenerador.genLABEL(identif);
    miLexer.Retroceder();  // He leido un s&iacute;mbolo de m&aacute;s
  }
}
</code></pre>

<p>El reconocer una orden GOTO no tiene mayor dificultad, y el generar el c&oacute;digo resultante para GOTO tiene dos casos, seg&uacute;n si ya se sabe la direcci&oacute;n de salto (porque es un salto hacia atr&aacute;s) o no se sabe (porque es un salto hacia adelante):</p>

<pre><code>
public void genGOTO(string identif)
{
    int direccion = miTablaSimb.LeerValor( identif );
    if (direccion != 0)  // Si ya se sabe la direcci&oacute;n de salto
      ficheroDestino.WriteLine(lineaActual
        + &quot; DATA C3,&quot;
        + Convert.ToString(direccion % 256,16).ToUpper() +&quot;,&quot;
        + Convert.ToString(direccion / 256,16).ToUpper()
        + &quot;: ' JP &quot; +identif );
    else // Si no se sabe la direcci&oacute;n de salto: en segunda pasada
      ficheroDestino.WriteLine(lineaActual
        + &quot; DATA C3,&quot;
        + &quot;nnn&quot;+identif+&quot;LO,&quot;
        + &quot;nnn&quot;+identif+&quot;HI&quot;
        + &quot;: ' JP &quot; +identif );
    lineaActual += 10; longitudTotal += 3;
}
</code></pre>

<p>He elegido un nombre para esas etiquetas de salto que sea &quot;poco probable&quot; encontrar por error: &quot;nnn&quot;, seguido de la etiqueta (por ejemplo, &quot;DESTINO1&quot;) y de &quot;LO&quot; para representar el byte menos significativo: nnnDESTINO1LO, o seguido de HI para el byte m&aacute;s siginificativo: nnnDESTINO1HI. Entonces, la segunda pasada podr&iacute;a reemplazar esas cadenas por sus valores, tomados de la tabla de s&iacute;mbolos, as&iacute;:</p>

<pre><code>
/* Segunda pasada: si hay saltos adelante, a direcciones indeterminadas */
void segundaPasada()
{
  string linea;
  string nuevoNombre = nombre.Substring(0, nombre.IndexOf(&quot;.&quot;) )
    + &quot;_2p.bas&quot;;
  
  StreamReader entrada = File.OpenText(nombre);
  StreamWriter salida = File.CreateText(nuevoNombre);
  linea = entrada.ReadLine();
  while (linea != null)
  {
    if (linea.IndexOf(&quot;nnn&quot;) &gt;= 0)  // Si contiene simbolos a sustituir
    {
      for (int i=0; i&lt;miTablaSimb.GetNumSimbolos(); i++)
      {
        string nombreVar1 = &quot;nnn&quot; + miTablaSimb.GetSimbolo(i)+&quot;LO&quot;; 
        string nombreVar2 = &quot;nnn&quot; + miTablaSimb.GetSimbolo(i)+&quot;HI&quot;;
        if (linea.IndexOf( nombreVar1 ) &gt;= 0)
          linea = linea.Replace( nombreVar1, 
            hex(miTablaSimb.GetValor(i) % 256) ); // LO = Byte menos signif.
        if (linea.IndexOf( nombreVar2 ) &gt;= 0)
          linea = linea.Replace( nombreVar2, 
            hex(miTablaSimb.GetValor(i) / 256) ); // HI = Byte mas signif.
      }
    }
    salida.WriteLine(linea);
    linea = entrada.ReadLine();
  }
  entrada.Close();
  salida.Close();
}
</code></pre>


<p>Por supuesto, hay alg&uacute;n cambio m&aacute;s, como el definir el tipo LABEL en nuestra tabla de s&iacute;mbolos, las funciones para leer el nombre o el valor de un s&iacute;mbolo a partir de su posici&oacute;n en dicha tabla, pero son cambios menores.</p>

<p><br>
  Como siempre, para m&aacute;s detalles, todo el c&oacute;digo est&aacute; en la p&aacute;gina del proyecto en Google Code:<br />
  <br />
  <a href="http://code.google.com/p/cpcpachi/">http://code.google.com/p/cpcpachi/</a></p>
<p align="right">(Volver al <a href="doc.html">&iacute;ndice</a>) </p>
</body>
</html>
